import logging
from typing import List, Dict, Tuple
from datetime import datetime, timedelta
import pandas as pd
import numpy as np
from collections import Counter, defaultdict
import matplotlib.pyplot as plt
import seaborn as sns
import io
import base64

from app import db
from app.models import Article, SocialMediaPost, SentimentHistory

class TrendAnalyzer:
    def __init__(self):
        self.logger = logging.getLogger(__name__)
    
    def analyze_sentiment_trend(self, days: int = 30) -> Dict:
        """
        分析情感趋势
        
        Args:
            days: 时间范围（天）
            
        Returns:
            情感趋势数据
        """
        try:
            # 获取指定时间范围内的情感统计数据
            end_date = datetime.now().date()
            start_date = end_date - timedelta(days=days)
            
            sentiment_history = SentimentHistory.query.filter(
                SentimentHistory.date >= start_date,
                SentimentHistory.date <= end_date
            ).order_by(SentimentHistory.date).all()
            
            # 准备数据
            dates = []
            positive_counts = []
            negative_counts = []
            neutral_counts = []
            
            for record in sentiment_history:
                dates.append(record.date.strftime('%Y-%m-%d'))
                positive_counts.append(record.positive_count)
                negative_counts.append(record.negative_count)
                neutral_counts.append(record.neutral_count)
            
            # 计算总量和比例
            total_counts = [p + n + u for p, n, u in zip(positive_counts, negative_counts, neutral_counts)]
            
            positive_ratio = [p / t if t > 0 else 0 for p, t in zip(positive_counts, total_counts)]
            negative_ratio = [n / t if t > 0 else 0 for n, t in zip(negative_counts, total_counts)]
            neutral_ratio = [u / t if t > 0 else 0 for u, t in zip(neutral_counts, total_counts)]
            
            # 生成图表
            fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10))
            
            # 绝对数量图表
            ax1.plot(dates, positive_counts, 'g-', label='正面')
            ax1.plot(dates, negative_counts, 'r-', label='负面')
            ax1.plot(dates, neutral_counts, 'b-', label='中性')
            ax1.set_title('情感绝对数量趋势')
            ax1.set_xlabel('日期')
            ax1.set_ylabel('数量')
            ax1.legend()
            ax1.grid(True)
            
            # 比例图表
            ax2.stackplot(dates, positive_ratio, neutral_ratio, negative_ratio, 
                          labels=['正面', '中性', '负面'],
                          colors=['green', 'blue', 'red'])
            ax2.set_title('情感比例趋势')
            ax2.set_xlabel('日期')
            ax2.set_ylabel('比例')
            ax2.legend()
            ax2.grid(True)
            
            plt.tight_layout()
            
            # 将图表转换为base64编码的字符串
            buffer = io.BytesIO()
            plt.savefig(buffer, format='png')
            buffer.seek(0)
            image_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
            plt.close()
            
            # 准备返回数据
            trend_data = {
                'dates': dates,
                'positive_counts': positive_counts,
                'negative_counts': negative_counts,
                'neutral_counts': neutral_counts,
                'total_counts': total_counts,
                'positive_ratio': positive_ratio,
                'negative_ratio': negative_ratio,
                'neutral_ratio': neutral_ratio,
                'chart_image': image_base64
            }
            
            return trend_data
            
        except Exception as e:
            self.logger.error(f"分析情感趋势失败: {str(e)}")
            return {}
    
    def analyze_source_distribution(self, days: int = 30) -> Dict:
        """
        分析来源分布
        
        Args:
            days: 时间范围（天）
            
        Returns:
            来源分布数据
        """
        try:
            # 获取指定时间范围内的文章
            start_date = datetime.now() - timedelta(days=days)
            
            # 获取新闻来源分布
            news_sources = db.session.query(Article.source, db.func.count(Article.id)).filter(
                Article.collected_at >= start_date
            ).group_by(Article.source).all()
            
            # 获取社交媒体平台分布
            social_platforms = db.session.query(SocialMediaPost.platform, db.func.count(SocialMediaPost.id)).filter(
                SocialMediaPost.collected_at >= start_date
            ).group_by(SocialMediaPost.platform).all()
            
            # 准备数据
            news_data = {source: count for source, count in news_sources}
            social_data = {platform: count for platform, count in social_platforms}
            
            # 生成图表
            fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 7))
            
            # 新闻来源饼图
            if news_data:
                ax1.pie(news_data.values(), labels=news_data.keys(), autopct='%1.1f%%')
                ax1.set_title('新闻来源分布')
            else:
                ax1.text(0.5, 0.5, '无数据', horizontalalignment='center', verticalalignment='center')
                ax1.set_title('新闻来源分布 (无数据)')
            
            # 社交媒体平台饼图
            if social_data:
                ax2.pie(social_data.values(), labels=social_data.keys(), autopct='%1.1f%%')
                ax2.set_title('社交媒体平台分布')
            else:
                ax2.text(0.5, 0.5, '无数据', horizontalalignment='center', verticalalignment='center')
                ax2.set_title('社交媒体平台分布 (无数据)')
            
            plt.tight_layout()
            
            # 将图表转换为base64编码的字符串
            buffer = io.BytesIO()
            plt.savefig(buffer, format='png')
            buffer.seek(0)
            image_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
            plt.close()
            
            # 准备返回数据
            distribution_data = {
                'news_sources': news_data,
                'social_platforms': social_data,
                'chart_image': image_base64
            }
            
            return distribution_data
            
        except Exception as e:
            self.logger.error(f"分析来源分布失败: {str(e)}")
            return {}
    
    def analyze_keyword_trend(self, keyword: str, days: int = 30) -> Dict:
        """
        分析关键词趋势
        
        Args:
            keyword: 关键词
            days: 时间范围（天）
            
        Returns:
            关键词趋势数据
        """
        try:
            # 获取指定时间范围内的文章和社交媒体帖子
            end_date = datetime.now()
            start_date = end_date - timedelta(days=days)
            
            # 按日期统计包含关键词的文章数量
            articles_by_date = defaultdict(int)
            social_posts_by_date = defaultdict(int)
            
            # 查询包含关键词的文章
            articles = Article.query.filter(
                Article.keywords.like(f'%{keyword}%'),
                Article.collected_at >= start_date,
                Article.collected_at <= end_date
            ).all()
            
            for article in articles:
                date_str = article.collected_at.strftime('%Y-%m-%d')
                articles_by_date[date_str] += 1
            
            # 查询包含关键词的社交媒体帖子
            social_posts = SocialMediaPost.query.filter(
                SocialMediaPost.keywords.like(f'%{keyword}%'),
                SocialMediaPost.collected_at >= start_date,
                SocialMediaPost.collected_at <= end_date
            ).all()
            
            for post in social_posts:
                date_str = post.collected_at.strftime('%Y-%m-%d')
                social_posts_by_date[date_str] += 1
            
            # 准备日期列表
            date_list = []
            current_date = start_date
            while current_date <= end_date:
                date_list.append(current_date.strftime('%Y-%m-%d'))
                current_date += timedelta(days=1)
            
            # 准备数据
            article_counts = [articles_by_date.get(date, 0) for date in date_list]
            social_post_counts = [social_posts_by_date.get(date, 0) for date in date_list]
            total_counts = [a + s for a, s in zip(article_counts, social_post_counts)]
            
            # 生成图表
            plt.figure(figsize=(12, 6))
            plt.plot(date_list, article_counts, 'b-', label='新闻文章')
            plt.plot(date_list, social_post_counts, 'g-', label='社交媒体')
            plt.plot(date_list, total_counts, 'r-', label='总计')
            plt.title(f'关键词 "{keyword}" 趋势分析')
            plt.xlabel('日期')
            plt.ylabel('数量')
            plt.legend()
            plt.grid(True)
            plt.xticks(rotation=45)
            plt.tight_layout()
            
            # 将图表转换为base64编码的字符串
            buffer = io.BytesIO()
            plt.savefig(buffer, format='png')
            buffer.seek(0)
            image_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
            plt.close()
            
            # 准备返回数据
            trend_data = {
                'keyword': keyword,
                'dates': date_list,
                'article_counts': article_counts,
                'social_post_counts': social_post_counts,
                'total_counts': total_counts,
                'chart_image': image_base64
            }
            
            return trend_data
            
        except Exception as e:
            self.logger.error(f"分析关键词趋势失败: {str(e)}")
            return {}
    
    def analyze_volume_trend(self, days: int = 30, interval: str = 'day') -> Dict:
        """
        分析声量趋势
        
        Args:
            days: 时间范围（天）
            interval: 时间间隔，支持 'day', 'week', 'month'
            
        Returns:
            声量趋势数据
        """
        try:
            # 获取指定时间范围内的文章和社交媒体帖子
            end_date = datetime.now()
            start_date = end_date - timedelta(days=days)
            
            # 查询文章
            articles = Article.query.filter(
                Article.collected_at >= start_date,
                Article.collected_at <= end_date
            ).all()
            
            # 查询社交媒体帖子
            social_posts = SocialMediaPost.query.filter(
                SocialMediaPost.collected_at >= start_date,
                SocialMediaPost.collected_at <= end_date
            ).all()
            
            # 按时间间隔统计
            if interval == 'day':
                format_str = '%Y-%m-%d'
                delta = timedelta(days=1)
            elif interval == 'week':
                format_str = '%Y-%W'  # ISO周
                delta = timedelta(days=7)
            elif interval == 'month':
                format_str = '%Y-%m'
                delta = timedelta(days=30)
            else:
                format_str = '%Y-%m-%d'
                delta = timedelta(days=1)
            
            # 按时间间隔统计文章和帖子数量
            articles_by_interval = defaultdict(int)
            social_posts_by_interval = defaultdict(int)
            
            for article in articles:
                interval_str = article.collected_at.strftime(format_str)
                articles_by_interval[interval_str] += 1
            
            for post in social_posts:
                interval_str = post.collected_at.strftime(format_str)
                social_posts_by_interval[interval_str] += 1
            
            # 准备时间间隔列表
            interval_list = []
            current_date = start_date
            while current_date <= end_date:
                interval_list.append(current_date.strftime(format_str))
                if interval == 'day':
                    current_date += timedelta(days=1)
                elif interval == 'week':
                    current_date += timedelta(days=7)
                elif interval == 'month':
                    # 简化处理，实际应用中可能需要更精确的月份计算
                    current_date += timedelta(days=30)
            
            # 去重
            interval_list = sorted(list(set(interval_list)))
            
            # 准备数据
            article_counts = [articles_by_interval.get(interval, 0) for interval in interval_list]
            social_post_counts = [social_posts_by_interval.get(interval, 0) for interval in interval_list]
            total_counts = [a + s for a, s in zip(article_counts, social_post_counts)]
            
            # 生成图表
            plt.figure(figsize=(12, 6))
            plt.bar(interval_list, article_counts, label='新闻文章', alpha=0.7)
            plt.bar(interval_list, social_post_counts, bottom=article_counts, label='社交媒体', alpha=0.7)
            plt.plot(interval_list, total_counts, 'r-', label='总计')
            plt.title('声量趋势分析')
            plt.xlabel('时间')
            plt.ylabel('数量')
            plt.legend()
            plt.grid(True)
            plt.xticks(rotation=45)
            plt.tight_layout()
            
            # 将图表转换为base64编码的字符串
            buffer = io.BytesIO()
            plt.savefig(buffer, format='png')
            buffer.seek(0)
            image_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
            plt.close()
            
            # 准备返回数据
            volume_data = {
                'intervals': interval_list,
                'article_counts': article_counts,
                'social_post_counts': social_post_counts,
                'total_counts': total_counts,
                'chart_image': image_base64
            }
            
            return volume_data
            
        except Exception as e:
            self.logger.error(f"分析声量趋势失败: {str(e)}")
            return {} 